Build examples into `target/examples`
authorAlex Crichton <alex@alexcrichton.com>
Fri, 24 Oct 2014 16:18:19 +0000 (09:18 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 27 Oct 2014 19:57:49 +0000 (12:57 -0700)
This ends up killing two birds with one stone! The rationale behind this is that
the example and bin namespaces are not the same, and we don't mix metadata into
either filename, so the outputs need to be in different locations.

Closes #193
Closes #751

src/cargo/core/manifest.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/fingerprint.rs
src/cargo/ops/cargo_rustc/layout.rs
src/cargo/ops/cargo_rustc/mod.rs
tests/test_cargo_compile.rs
tests/test_cargo_test.rs

index e274dbbc260a79ce7b080df4c1bb0e883d09549c..e2102ac4d54b0eb6dd1a201673c5cdf1b7e791ec 100644 (file)
@@ -104,7 +104,8 @@ impl LibKind {
 #[deriving(Show, Clone, Hash, PartialEq, Encodable)]
 pub enum TargetKind {
     LibTarget(Vec<LibKind>),
-    BinTarget
+    BinTarget,
+    ExampleTarget,
 }
 
 #[deriving(Encodable, Decodable, Clone, PartialEq, Show)]
@@ -328,7 +329,8 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Target {
     fn encode(&self, s: &mut S) -> Result<(), E> {
         let kind = match self.kind {
             LibTarget(ref kinds) => kinds.iter().map(|k| k.crate_type()).collect(),
-            BinTarget => vec!("bin")
+            BinTarget => vec!("bin"),
+            ExampleTarget => vec!["example"],
         };
 
         SerializedTarget {
@@ -457,7 +459,7 @@ impl Target {
 
     pub fn example_target(name: &str, src_path: &Path, profile: &Profile) -> Target {
         Target {
-            kind: BinTarget,
+            kind: ExampleTarget,
             name: name.to_string(),
             src_path: src_path.clone(),
             profile: profile.clone(),
@@ -524,7 +526,7 @@ impl Target {
         }
     }
 
-    /// Returns true for binary, bench, tests and examples.
+    /// Returns true for binary, bench, and tests.
     pub fn is_bin(&self) -> bool {
         match self.kind {
             BinTarget => true,
@@ -532,6 +534,14 @@ impl Target {
         }
     }
 
+    /// Returns true for exampels
+    pub fn is_example(&self) -> bool {
+        match self.kind {
+            ExampleTarget => true,
+            _ => false
+        }
+    }
+
     pub fn get_profile(&self) -> &Profile {
         &self.profile
     }
@@ -546,7 +556,8 @@ impl Target {
             LibTarget(ref kinds) => {
                 kinds.iter().map(|kind| kind.crate_type()).collect()
             },
-            BinTarget => vec!("bin")
+            ExampleTarget |
+            BinTarget => vec!("bin"),
         }
     }
 }
index e3762012ffaf94b325e0ba760d1d4dcf1de383af..fb3886871cc708acf39f1c9094a85aaa756e1d05 100644 (file)
@@ -225,7 +225,9 @@ impl<'a, 'b> Context<'a, 'b> {
         let stem = target.file_stem();
 
         let mut ret = Vec::new();
-        if target.is_bin() || target.get_profile().is_test() {
+        if target.is_example() {
+            ret.push(format!("examples/{}{}", stem, self.target_exe));
+        } else if target.is_bin() || target.get_profile().is_test() {
             ret.push(format!("{}{}", stem, self.target_exe));
         } else {
             if target.is_dylib() {
index 4027d3a2fbfe83c29fb936eaee27b21b7affffb0..1fd7b5534522f27724a33b3c6dab7af8d0fb3746 100644 (file)
@@ -87,7 +87,11 @@ pub fn prepare_target(cx: &mut Context, pkg: &Package, target: &Target,
 
     let (old_root, root) = {
         let layout = cx.layout(pkg, kind);
-        (layout.old_root().clone(), layout.root().clone())
+        if target.is_example() {
+            (layout.old_examples().clone(), layout.examples().clone())
+        } else {
+            (layout.old_root().clone(), layout.root().clone())
+        }
     };
     let mut pairs = vec![(old_loc, new_loc.clone())];
     if !target.get_profile().is_doc() {
index a9e9ca29473e09935bdbf11932ebf864b65be8ae..b2fd7bf8822e141babe6c5d664c6492e0f37d232 100644 (file)
@@ -11,6 +11,9 @@
 //!     # This is the root directory for all output of *dependencies*
 //!     deps/
 //!
+//!     # Root directory for all compiled examples
+//!     examples/
+//!
 //!     # This is the location at which the output of all custom build
 //!     # commands are rooted
 //!     native/
@@ -44,6 +47,7 @@
 //!     # Same as the two above old directories
 //!     old-native/
 //!     old-fingerprint/
+//!     old-examples/
 //! ```
 
 use std::io::{mod, fs, IoResult};
@@ -57,11 +61,13 @@ pub struct Layout {
     deps: Path,
     native: Path,
     fingerprint: Path,
+    examples: Path,
 
     old_deps: Path,
     old_root: Path,
     old_native: Path,
     old_fingerprint: Path,
+    old_examples: Path,
 }
 
 pub struct LayoutProxy<'a> {
@@ -88,10 +94,12 @@ impl Layout {
             deps: root.join("deps"),
             native: root.join("native"),
             fingerprint: root.join(".fingerprint"),
+            examples: root.join("examples"),
             old_deps: root.join("old-deps"),
             old_root: root.join("old-root"),
             old_native: root.join("old-native"),
             old_fingerprint: root.join("old-fingerprint"),
+            old_examples: root.join("old-examples"),
             root: root,
         }
     }
@@ -101,31 +109,16 @@ impl Layout {
             try!(fs::mkdir_recursive(&self.root, io::USER_RWX));
         }
 
-        if self.old_deps.exists() {
-            try!(fs::rmdir_recursive(&self.old_deps));
-        }
+        try!(old(&[
+            (&self.old_deps, &self.deps),
+            (&self.old_native, &self.native),
+            (&self.old_fingerprint, &self.fingerprint),
+            (&self.old_examples, &self.examples),
+        ]));
+
         if self.old_root.exists() {
             try!(fs::rmdir_recursive(&self.old_root));
         }
-        if self.old_native.exists() {
-            try!(fs::rmdir_recursive(&self.old_native));
-        }
-        if self.old_fingerprint.exists() {
-            try!(fs::rmdir_recursive(&self.old_fingerprint));
-        }
-        if self.deps.exists() {
-            try!(fs::rename(&self.deps, &self.old_deps));
-        }
-        if self.native.exists() {
-            try!(fs::rename(&self.native, &self.old_native));
-        }
-        if self.fingerprint.exists() {
-            try!(fs::rename(&self.fingerprint, &self.old_fingerprint));
-        }
-
-        try!(fs::mkdir(&self.deps, io::USER_RWX));
-        try!(fs::mkdir(&self.native, io::USER_RWX));
-        try!(fs::mkdir(&self.fingerprint, io::USER_RWX));
         try!(fs::mkdir(&self.old_root, io::USER_RWX));
 
         for file in try!(fs::readdir(&self.root)).iter() {
@@ -134,11 +127,25 @@ impl Layout {
             try!(fs::rename(file, &self.old_root.join(file.filename().unwrap())));
         }
 
-        Ok(())
+        return Ok(());
+
+        fn old(dirs: &[(&Path, &Path)]) -> IoResult<()> {
+            for &(old, new) in dirs.iter() {
+                if old.exists() {
+                    try!(fs::rmdir_recursive(old));
+                }
+                if new.exists() {
+                    try!(fs::rename(new, old));
+                }
+                try!(fs::mkdir(new, io::USER_DIR));
+            }
+            Ok(())
+        }
     }
 
     pub fn dest<'a>(&'a self) -> &'a Path { &self.root }
     pub fn deps<'a>(&'a self) -> &'a Path { &self.deps }
+    pub fn examples<'a>(&'a self) -> &'a Path { &self.examples }
     pub fn native(&self, package: &Package) -> Path {
         self.native.join(self.pkg_dir(package))
     }
@@ -148,6 +155,7 @@ impl Layout {
 
     pub fn old_dest<'a>(&'a self) -> &'a Path { &self.old_root }
     pub fn old_deps<'a>(&'a self) -> &'a Path { &self.old_deps }
+    pub fn old_examples<'a>(&'a self) -> &'a Path { &self.old_examples }
     pub fn old_native(&self, package: &Package) -> Path {
         self.old_native.join(self.pkg_dir(package))
     }
@@ -166,6 +174,7 @@ impl Drop for Layout {
         let _ = fs::rmdir_recursive(&self.old_root);
         let _ = fs::rmdir_recursive(&self.old_native);
         let _ = fs::rmdir_recursive(&self.old_fingerprint);
+        let _ = fs::rmdir_recursive(&self.old_examples);
     }
 }
 
@@ -182,12 +191,16 @@ impl<'a> LayoutProxy<'a> {
     }
     pub fn deps(&self) -> &'a Path { self.root.deps() }
 
+    pub fn examples(&self) -> &'a Path { self.root.examples() }
+
     pub fn native(&self, pkg: &Package) -> Path { self.root.native(pkg) }
 
     pub fn old_root(&self) -> &'a Path {
         if self.primary {self.root.old_dest()} else {self.root.old_deps()}
     }
 
+    pub fn old_examples(&self) -> &'a Path { self.root.old_examples() }
+
     pub fn old_native(&self, pkg: &Package) -> Path {
         self.root.old_native(pkg)
     }
index 45d51b9dc586e9b48b46401e3e8acc0b9383fae2..ec34c060daabe715a5e871330252e2a5517dde21 100644 (file)
@@ -431,7 +431,11 @@ fn build_base_args(cx: &Context,
 fn build_plugin_args(mut cmd: ProcessBuilder, cx: &Context, pkg: &Package,
                      target: &Target, kind: Kind) -> ProcessBuilder {
     cmd = cmd.arg("--out-dir");
-    cmd = cmd.arg(cx.layout(pkg, kind).root());
+    if target.is_example() {
+        cmd = cmd.arg(cx.layout(pkg, kind).examples());
+    } else {
+        cmd = cmd.arg(cx.layout(pkg, kind).root());
+    }
 
     let (_, dep_info_loc) = fingerprint::dep_info_loc(cx, pkg, target, kind);
     cmd = cmd.arg("--dep-info").arg(dep_info_loc);
index afb48727d07bfba4a1ae066e1867370e6483dcc6..e4abdd389cd6de144f9eb600f1676856fb212355 100644 (file)
@@ -975,7 +975,7 @@ test!(many_crate_types_old_style_lib_location {
     let files = fs::readdir(&p.root().join("target")).assert();
     let mut files: Vec<String> = files.iter().filter_map(|f| {
         match f.filename_str().unwrap() {
-            "deps" => None,
+            "examples" | "deps" => None,
             s if s.contains("fingerprint") || s.contains("dSYM") => None,
             s => Some(s.to_string())
         }
@@ -1013,7 +1013,7 @@ test!(many_crate_types_correct {
     let files = fs::readdir(&p.root().join("target")).assert();
     let mut files: Vec<String> = files.iter().filter_map(|f| {
         match f.filename_str().unwrap() {
-            "deps" => None,
+            "examples" | "deps" => None,
             s if s.contains("fingerprint") || s.contains("dSYM") => None,
             s => Some(s.to_string())
         }
@@ -1280,8 +1280,10 @@ test!(explicit_examples {
         "#);
 
     assert_that(p.cargo_process("test"), execs());
-    assert_that(process(p.bin("hello")), execs().with_stdout("Hello, World!\n"));
-    assert_that(process(p.bin("goodbye")), execs().with_stdout("Goodbye, World!\n"));
+    assert_that(process(p.bin("examples/hello")),
+                        execs().with_stdout("Hello, World!\n"));
+    assert_that(process(p.bin("examples/goodbye")),
+                        execs().with_stdout("Goodbye, World!\n"));
 })
 
 test!(implicit_examples {
@@ -1307,8 +1309,10 @@ test!(implicit_examples {
         "#);
 
     assert_that(p.cargo_process("test"), execs().with_status(0));
-    assert_that(process(p.bin("hello")), execs().with_stdout("Hello, World!\n"));
-    assert_that(process(p.bin("goodbye")), execs().with_stdout("Goodbye, World!\n"));
+    assert_that(process(p.bin("examples/hello")),
+                execs().with_stdout("Hello, World!\n"));
+    assert_that(process(p.bin("examples/goodbye")),
+                execs().with_stdout("Goodbye, World!\n"));
 })
 
 test!(standard_build_no_ndebug {
index fd11bd361f10d11b55894ce2f088884b9153d83e..34c045449828849b6b540242f186c2811823fda6 100644 (file)
@@ -1150,3 +1150,32 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
 ", compiling = COMPILING, running = RUNNING, dir = p.url(),
    doctest = DOCTEST).as_slice()));
 })
+
+test!(example_bin_same_name {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("src/bin/foo.rs", r#"fn main() { println!("bin"); }"#)
+        .file("examples/foo.rs", r#"fn main() { println!("example"); }"#);
+
+    assert_that(p.cargo_process("test").arg("--no-run").arg("-v"),
+                execs().with_status(0)
+                       .with_stdout(format!("\
+{compiling} foo v0.0.1 ({dir})
+{running} `rustc [..]bin[..]foo.rs [..] --test [..]`
+{running} `rustc [..]bin[..]foo.rs [..]`
+{running} `rustc [..]examples[..]foo.rs [..]`
+", compiling = COMPILING, running = RUNNING, dir = p.url()).as_slice()));
+
+    assert_that(&p.bin("foo"), existing_file());
+    assert_that(&p.bin("examples/foo"), existing_file());
+
+    assert_that(p.process(p.bin("foo")),
+                execs().with_status(0).with_stdout("bin\n"));
+    assert_that(p.process(p.bin("examples/foo")),
+                execs().with_status(0).with_stdout("example\n"));
+})